/*
 * GuiSAT.java
 *
 * Created on Jun 26, 2009, 12:29:11 PM
 */
package guiSAT;

import geneticsatsolver.Problem;
import geneticsatsolver.Solver;
import geneticsatsolver.Status;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.util.Iterator;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.JFileChooser;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.RectangleInsets;

/**
 * Class GuiSAT provide a graphical user interface for SAT problem solving
 * 
 * @author igor
 */
public class GuiSAT extends javax.swing.JFrame implements Runnable {

    Solver solver = null;
    Thread runner = null;
    boolean done = false;

    /** Creates new form GuiSAT */
    public GuiSAT() {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            SwingUtilities.updateComponentTreeUI(this);
        } catch (Exception ed) {
            ed.printStackTrace();
        }
        initComponents();
        jTextField1.setText(System.getProperty("user.dir"));
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jPanel1 = new javax.swing.JPanel();
        jButtonStart = new javax.swing.JButton();
        jButtonStop = new javax.swing.JButton();
        jPanel2 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        jSpinnerDie = new javax.swing.JSpinner();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        jLabel4 = new javax.swing.JLabel();
        jSpinnerMut = new javax.swing.JSpinner();
        jSpinnerCross = new javax.swing.JSpinner();
        jSpinnerPop = new javax.swing.JSpinner();
        jTextField1 = new javax.swing.JTextField();
        jButtonBrowse = new javax.swing.JButton();
        jLabel5 = new javax.swing.JLabel();
        jLabel6 = new javax.swing.JLabel();
        jSpinnerSelection = new javax.swing.JSpinner();
        jLabel7 = new javax.swing.JLabel();
        jSpinnerCrossrate = new javax.swing.JSpinner();
        jPanel3 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTextAreaSolution = new javax.swing.JTextArea();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Genetic SAT Solver");
        setResizable(false);

        jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("Fitness"));
        jPanel1.setPreferredSize(new java.awt.Dimension(520, 320));

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 532, Short.MAX_VALUE)
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 467, Short.MAX_VALUE)
        );

        jButtonStart.setText("Start");
        jButtonStart.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonStartActionPerformed(evt);
            }
        });

        jButtonStop.setText("Stop");
        jButtonStop.setEnabled(false);
        jButtonStop.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonStopActionPerformed(evt);
            }
        });

        jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder("Configuration"));

        jLabel1.setText("Population Size");

        jSpinnerDie.setModel(new javax.swing.SpinnerNumberModel(25, 0, 50, 1));

        jLabel2.setText("Die Rate (%)");

        jLabel3.setText("Mutation Rate");

        jLabel4.setText("Crossover Type");

        jSpinnerMut.setModel(new javax.swing.SpinnerNumberModel(0.2d, 0.0d, 1.0d, 0.01d));

        jSpinnerCross.setModel(new javax.swing.SpinnerListModel(new String[] {"Map", "Single Point"}));

        jSpinnerPop.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(100), Integer.valueOf(10), null, Integer.valueOf(10)));

        jButtonBrowse.setText("Browse...");
        jButtonBrowse.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButtonBrowseActionPerformed(evt);
            }
        });

        jLabel5.setText("Select DIMACS cnf format file");

        jLabel6.setText("Selection Method");

        jSpinnerSelection.setModel(new javax.swing.SpinnerListModel(new String[] {"Roulette Wheel", "Rank Selection"}));

        jLabel7.setText("Crossover Rate");

        jSpinnerCrossrate.setModel(new javax.swing.SpinnerNumberModel(0.95d, 0.0d, 1.0d, 0.01d));

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jTextField1, javax.swing.GroupLayout.DEFAULT_SIZE, 269, Short.MAX_VALUE)
                    .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addComponent(jLabel5, javax.swing.GroupLayout.Alignment.TRAILING)
                        .addComponent(jButtonBrowse))
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
                        .addGap(13, 13, 13)
                        .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                            .addComponent(jLabel2)
                            .addComponent(jLabel3)
                            .addComponent(jLabel1))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jSpinnerPop, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 146, Short.MAX_VALUE)
                            .addComponent(jSpinnerMut, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 146, Short.MAX_VALUE)
                            .addComponent(jSpinnerDie, javax.swing.GroupLayout.DEFAULT_SIZE, 146, Short.MAX_VALUE)))
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                            .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addGroup(jPanel2Layout.createSequentialGroup()
                                    .addGap(13, 13, 13)
                                    .addComponent(jLabel4))
                                .addComponent(jLabel6))
                            .addComponent(jLabel7))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                            .addComponent(jSpinnerCross, javax.swing.GroupLayout.DEFAULT_SIZE, 146, Short.MAX_VALUE)
                            .addComponent(jSpinnerSelection, javax.swing.GroupLayout.DEFAULT_SIZE, 146, Short.MAX_VALUE)
                            .addComponent(jSpinnerCrossrate, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 146, Short.MAX_VALUE))))
                .addContainerGap())
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addComponent(jLabel5)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jButtonBrowse)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jSpinnerPop, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel1))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel2)
                    .addComponent(jSpinnerDie, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel3)
                    .addComponent(jSpinnerMut, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jSpinnerCrossrate, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel7))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 22, Short.MAX_VALUE)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jSpinnerCross, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel4))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jSpinnerSelection, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel6))
                .addContainerGap())
        );

        jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder("Solution"));

        jTextAreaSolution.setColumns(4);
        jTextAreaSolution.setEditable(false);
        jTextAreaSolution.setLineWrap(true);
        jTextAreaSolution.setRows(4);
        jScrollPane1.setViewportView(jTextAreaSolution);

        javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
        jPanel3.setLayout(jPanel3Layout);
        jPanel3Layout.setHorizontalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel3Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 269, Short.MAX_VALUE)
                .addContainerGap())
        );
        jPanel3Layout.setVerticalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel3Layout.createSequentialGroup()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(14, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jButtonStart, javax.swing.GroupLayout.PREFERRED_SIZE, 106, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 89, Short.MAX_VALUE)
                        .addComponent(jButtonStop, javax.swing.GroupLayout.PREFERRED_SIZE, 110, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 544, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jPanel1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 503, Short.MAX_VALUE)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jButtonStart)
                            .addComponent(jButtonStop))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void jButtonStartActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonStartActionPerformed
        // TODO add your handling code here:
        int crossType = 0;
        int selType = 0;
        if (jSpinnerCross.getValue().equals("Map")) {
            crossType = 1;
        } else {
            crossType = 0;
        }

        if (jSpinnerSelection.getValue().equals("Random")) {
            selType = 0;
        } else if (jSpinnerSelection.getValue().equals("Rank Selection")) {
            selType = 1;
        } else if (jSpinnerSelection.getValue().equals("Roulette Wheel")) {
            selType = 2;
        }
        Problem problem = new Problem(this.jTextField1.getText());
        if (problem.done) {
            this.jTextAreaSolution.setText("");
            this.jButtonStart.setEnabled(false);
            this.jButtonBrowse.setEnabled(false);
            this.jButtonStop.setEnabled(true);
            solver = new Solver(problem, (Integer) ((Integer) jSpinnerPop.getValue()/10)*10,
                    (Double) jSpinnerMut.getValue(), crossType, (Integer) jSpinnerDie.getValue(),
                    selType, (Double) jSpinnerCrossrate.getValue());
            solver.start();
            runner = new Thread(this);
            runner.start();
        } else {
            this.jTextAreaSolution.setText("Unable to load problem file!");
        }
}//GEN-LAST:event_jButtonStartActionPerformed

    private void jButtonBrowseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonBrowseActionPerformed
        JFileChooser fc = new JFileChooser(jTextField1.getText());
        int returnVal = fc.showOpenDialog(null);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            this.jTextField1.setText(fc.getSelectedFile().getAbsolutePath());
        }
}//GEN-LAST:event_jButtonBrowseActionPerformed

    private void jButtonStopActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonStopActionPerformed
        this.done();
        solver.done();
        this.jButtonStart.setEnabled(true);
        this.jButtonBrowse.setEnabled(true);
        this.jButtonStop.setEnabled(false);
}//GEN-LAST:event_jButtonStopActionPerformed

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new GuiSAT().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jButtonBrowse;
    private javax.swing.JButton jButtonStart;
    private javax.swing.JButton jButtonStop;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JSpinner jSpinnerCross;
    private javax.swing.JSpinner jSpinnerCrossrate;
    private javax.swing.JSpinner jSpinnerDie;
    private javax.swing.JSpinner jSpinnerMut;
    private javax.swing.JSpinner jSpinnerPop;
    private javax.swing.JSpinner jSpinnerSelection;
    private javax.swing.JTextArea jTextAreaSolution;
    private javax.swing.JTextField jTextField1;
    // End of variables declaration//GEN-END:variables

    private void done() {
        done = true;
    }

    /**
     * Thread started to manage polling of current status
     * and update the GUI
     */
    public void run() {
        Status status = solver.getStatus();
        boolean solved = status.solved;
        Vector<Status> statuss = new Vector<Status>();
        int lastgen = 0;

        while (!solved && !done) {
            status = solver.getStatus();
            if (status.generation > lastgen) {
                solved = status.solved;
                statuss.add(status);
                XYSeries maxFitness = new XYSeries("Max Fitness");
                XYSeries minFitness = new XYSeries("Min Fitness");
                XYSeries meanFitness = new XYSeries("Mean Fitness");

                Iterator<Status> iter = statuss.iterator();

                int i = 0;
                while (iter.hasNext()) {
                    i++;
                    status = iter.next();
                    maxFitness.add(status.generation, status.maxFitness);
                    minFitness.add(status.generation, status.minFitness);
                    meanFitness.add(status.generation, status.meanFitness);
                }

                XYSeriesCollection xyseriescollection = new XYSeriesCollection();
                xyseriescollection.addSeries(maxFitness);
                xyseriescollection.addSeries(minFitness);
                xyseriescollection.addSeries(meanFitness);
                JFreeChart chart = ChartFactory.createXYLineChart("Fitness vs Generations",
                        "Generations", "Fitness", xyseriescollection,
                        PlotOrientation.VERTICAL, true, true, false);
                XYPlot xyplot = (XYPlot) chart.getPlot();
                xyplot.setBackgroundPaint(Color.white);
                xyplot.setAxisOffset(new RectangleInsets(5D, 5D, 5D, 5D));
                XYLineAndShapeRenderer xylineandshaperenderer = (XYLineAndShapeRenderer) xyplot.getRenderer();

                xylineandshaperenderer.setSeriesShapesVisible(0, false);
                xylineandshaperenderer.setSeriesPaint(0, Color.BLUE);
                xylineandshaperenderer.setSeriesShapesVisible(0, false);
                xylineandshaperenderer.setSeriesPaint(0, Color.RED);
                xylineandshaperenderer.setSeriesShapesVisible(0, false);
                xylineandshaperenderer.setSeriesPaint(0, Color.GREEN);

                NumberAxis numberaxis = (NumberAxis) xyplot.getRangeAxis();
                numberaxis.setAutoRangeIncludesZero(false);
                numberaxis.setStandardTickUnits(NumberAxis.createStandardTickUnits());

                final ChartPanel chartPanel = new ChartPanel(chart);
                chartPanel.setPreferredSize(new java.awt.Dimension(510, 310));

                chartPanel.setBorder(BorderFactory.createLoweredBevelBorder());

                jPanel1.setLayout(new BorderLayout());
                jPanel1.setPreferredSize(new Dimension(520, 320));

                jPanel1.removeAll();
                jPanel1.add(chartPanel, BorderLayout.CENTER);
                jPanel1.validate();
                jPanel1.repaint();
                lastgen = status.generation;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException ex) {
                Logger.getLogger(GuiSAT.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        if (solved) {
            boolean[] ch = status.thebest.getChromosome();
            String solution = "";
            for (int k = 0; k < ch.length; k++) {
                if (ch[k]) {
                    solution += Integer.toString(k + 1) + " ";
                } else {
                    solution += Integer.toString(-1 * (k + 1)) + " ";
                }
            }
            jTextAreaSolution.setText(solution);
        }
        jButtonStart.setEnabled(true);
        jButtonBrowse.setEnabled(true);
        jButtonStop.setEnabled(false);
        done = false;
        runner = null;
    }
}
